package asteroids.model.programs.statement;

import java.util.ArrayList;
import java.util.Map;

import be.kuleuven.cs.som.annotate.*;
import asteroids.model.programs.expression.*;
import asteroids.model.programs.type.*;

public class If extends S {

	private E condition;
	private S then;
	private S otherwise;
	private S actualClause;
	private boolean hasBeenInitialised;
	
	public If(int line, int column, E condition, S then, S otherwise) {
		super(line, column);
		this.condition = condition;
		this.then = then;
		this.otherwise = otherwise;
		this.hasBeenInitialised = false;
		super.setExecuted(false);
	}

	@Basic
	public E getCondition(){
		return condition;
	}
	
	@Basic
	public S getThen(){
		return then;
	}
	
	@Basic
	public S getOtherwise(){
		return otherwise;
	}
	
	private boolean isConditionTrue(){
		if(getCondition().getValue() instanceof Bool)
			return ((Bool) getCondition().getValue()).getValue();
		return false;
	}
	
	private void setActualClause(){ //choose between 'then' or 'otherwise' clause
		if(isConditionTrue()){
			actualClause = then;
		}
		else{
			actualClause = otherwise;
		}
		this.hasBeenInitialised = true;
		
	}
	
	//does nothing: if has no actual execution, if just gives back his body
	@Override
	public void execute(T type){
	}
	
	@Override
	public S getNextStatement(){
		if(hasBeenExecuted()){ //we have no more statement to execute
			super.setExecuted(true);
			return null;
		}
		if(!hasBeenInitialised){ //beginning of the program, initialise actualClause
			setActualClause();
		}
		if(hasSteps()){
			S s = actualClause.getNextStatement();
			if(s == actualClause.getFirstStatement()){ //if we will give the first statement, we want to
				setActualClause(); 			//check the condition to be sure we are in the right clause.
				actualClause.setExecuted(false);  //reset the first statement to non-executed
				return actualClause.getNextStatement();
			}
			else
				return s;
		}
		return null;
	}

	@Override
	public boolean hasSteps() {
		if(!hasBeenInitialised)
			setActualClause();
		return actualClause.hasSteps();
	}

	
	@Override
	public boolean hasBeenExecuted(){
		if(!hasBeenInitialised)
			setActualClause();
		return actualClause.hasBeenExecuted();
	}
	
	@Override
	public void setExecuted(boolean b){
		then.setExecuted(b);
		otherwise.setExecuted(b);
		super.setExecuted(b);
	}
	
	@Override
	public S getFirstStatement(){
		if(isConditionTrue())
			return then.getFirstStatement();
		else
			return otherwise.getFirstStatement();
	}
	
	@Override
	public S getLastStatement(){
		if(isConditionTrue())
			return then.getLastStatement();
		else
			return otherwise.getLastStatement();
	}

	@Override
	public ArrayList<E> getExpressions(){
		ArrayList<E> list = new ArrayList<E>();
		list.add(condition);
		list.addAll(then.getExpressions());
		list.addAll(otherwise.getExpressions());
		return list;
	}
	
	@Override
	public ArrayList<VariableExpression> getVariableExpressions(){
		ArrayList<VariableExpression> list = getCondition().getVariables();
		list.addAll(getThen().getVariableExpressions());
		list.addAll(getOtherwise().getVariableExpressions());
		return list;
	}
	
	public String typecheck(Map<String, T> globals){
		String cTypeCheck = getCondition().typecheck();
		String thenTypeCheck = getThen().typecheck(globals);
		String elseTypeCheck = getOtherwise().typecheck(globals);
		if(!(thenTypeCheck + elseTypeCheck + cTypeCheck).equals(""))
			return thenTypeCheck + elseTypeCheck + cTypeCheck;
		T type = getCondition().getValue();
		if(type.getClass() != Bool.class)
			return "Typecast error at line " + getLine() + ", column " + getColumn() + ".\n";
		return "";
	}
	
	public boolean hasAction(){
		return getThen().hasAction() || getOtherwise().hasAction();
	}
}